package socket;

import java.io.IOException;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class Server implements Runnable{
    private static final Map<String,Socket> map=new ConcurrentHashMap<>();
    private final Socket socket;
    StringBuilder online;
    public Server(Socket socket){
        this.socket=socket;
    }
    @Override
    public void run() {
        //1.获取客户端的输入流
        try {
            Scanner scanner=new Scanner(socket.getInputStream());
            String msg;
            while(true){
                if(scanner.hasNextLine()){
                    //0.处理客户端输入的字符串
                    msg=scanner.nextLine();
                    Pattern pattern=Pattern.compile("\r");
                    Matcher matcher=pattern.matcher(msg);
                    msg=matcher.replaceAll("");
                    //1.注册用户流程,注册用户的格式为:userName:用户名
                    if(msg.startsWith("userName:")){
                        //将用户名保存在userName中
                        String userName=msg.split(":")[1];
                        //注册该用户
                        userRegist(userName,socket);
                    }
                    //2.群聊信息流程,群聊的格式为:G:群聊信息
                    else if(msg.startsWith("G:")){
                        //必须先注册才可以!
                        firstStep(socket);
                        //保存群聊信息
                        String str=msg.split(":")[1];
                        //发送群聊信息
                        groupChat(socket,str);
                    }
                    //3.私聊信息流程,私聊的格式为:P:userName-私聊信息
                    else if(msg.startsWith("P:")&&msg.contains("-")){
                        //必须先注册才可以!
                        firstStep(socket);
                        //保存需要私聊的用户名
                        String userName=msg.split(":")[1].split("-")[0];
                        //保存私聊的信息
                        String str=msg.split(":")[1].split("-")[1];
                        //发送私聊信息
                        privateChat(socket,userName,str);
                    }
                    //4.用户退出流程,用户退出格式为:包含exit
                    else if(msg.contains("exit")){
                        //必须先注册才可以!
                        firstStep(socket);
                        userExit(socket);
                    }
                    else if (msg.contains("查询在线列表")){
                        online = new StringBuilder();
                        System.out.println(online);
                        Set<Map.Entry<String,Socket>> set=map.entrySet();
                        for (String key: map.keySet()) {
                            online.append("-").append(key);
                        }
                        //3.遍历Set集合将群聊信息发给每一个客户端
                        for(Map.Entry<String,Socket> entry:set){
                            //取得客户端的Socket对象
                            Socket client=entry.getValue();
                            //取得client客户端的输出流
                            PrintStream printStream=new PrintStream(client.getOutputStream());
                            printStream.println("【在线人数】："+online);
                        }
                    }
                    //其他输入格式均错误
                    else{
                        PrintStream printStream=new PrintStream(socket.getOutputStream());
                        printStream.println("【发送消息】出现错误");
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 第一步必须先注册!
     * @param socket 当前客户端
     */
    private void firstStep(Socket socket) throws IOException {
        Set<Map.Entry<String,Socket>> set=map.entrySet();
        for(Map.Entry<String,Socket> entry:set){
            if(entry.getValue().equals(socket)){
                if(entry.getKey()==null){
                    PrintStream printStream=new PrintStream(socket.getOutputStream());
                    printStream.println("【发送消息】出现错误");
                }
            }
        }
    }
    /**
     * 注册用户信息
     * @param userName 用户名
     * @param socket 用户客户端Socket对象
     */
    private void userRegist(String userName,Socket socket) throws IOException {
        map.put(userName,socket);
        System.out.println("[用户名为"+userName+"][客户端为"+socket+"]上线了!");
        System.out.println("当前在线人数为:"+map.size()+"人");
    }

    /**
     * 群聊流程(将Map集合转换为Set集合,从而取得每个客户端Socket,将群聊信息发送给每个客户端)
     * @param socket 发出群聊的客户端
     * @param msg 群聊信息
     */
    private void groupChat(Socket socket,String msg) throws IOException {
        //1.将Map集合转换为Set集合
        Set<Map.Entry<String,Socket>> set=map.entrySet();
        //2.遍历Set集合找到发起群聊信息的用户
        String userName = "";
        for(Map.Entry<String,Socket> entry:set){
            if(entry.getValue().equals(socket)){
                userName=entry.getKey();
                break;
            }
        }
        //3.遍历Set集合将群聊信息发给每一个客户端
        for(Map.Entry<String,Socket> entry:set){
            //取得客户端的Socket对象
            Socket client=entry.getValue();
            if (userName.equals("")){
                break;
            }
            //取得client客户端的输出流
            System.out.println("msg"+"【发送消息】：" + userName +"："+msg);
            PrintStream printStream=new PrintStream(client.getOutputStream());
            printStream.println("【发送消息】：" + userName +"："+msg);
        }
    }
    /**
     * 私聊流程(利用userName取得客户端的Socket对象,从而取得对应输出流,将私聊信息发送到指定客户端)
     * @param socket 当前客户端
     * @param userName 私聊的用户名
     * @param msg 私聊的信息
     */
    private void privateChat(Socket socket,String userName,String msg) throws IOException {
        //1.取得当前客户端的用户名
        String curUser=null;
        Set<Map.Entry<String,Socket>> set=map.entrySet();
        for(Map.Entry<String,Socket> entry:set){
            if(entry.getValue().equals(socket)){
                curUser=entry.getKey();
                break;
            }
        }

        //2.取得私聊用户名对应的客户端
        Socket client=map.get(userName);
        //3.获取私聊客户端的输出流,将私聊信息发送到指定客户端
        PrintStream printStream=new PrintStream(client.getOutputStream());
        printStream.println("【发送消息】：" + curUser+"对你私聊说:"+msg);

        client = map.get(curUser);
        printStream=new PrintStream(client.getOutputStream());
        printStream.println("【发送消息】：你对"+ userName +"私聊说:"+msg);
    }

    /**
     * 用户退出
     */
    private void userExit(Socket socket) {
        //1.利用socket取得对应的Key值
        String userName=null;
        for(String key:map.keySet()){
            if(map.get(key).equals(socket)){
                userName=key;
                break;
            }
        }
        //2.将userName,Socket元素从map集合中删除
        map.remove(userName,socket);
        //3.提醒服务器该客户端已下线
        System.out.println("用户:"+userName+"已下线!");
    }
}

public class MultiServer {
    public static void main(String[] args){
        try {
            //1.创建服务器端的ServerSocket对象,等待客户端连接
            ServerSocket serverSocket=new ServerSocket(6666);
            //2.创建线程池,从而可以处理多个客户端
            ExecutorService executorService= Executors.newFixedThreadPool(20);
            for(int i=0;i<20;i++){
                System.out.println("欢迎来到我的聊天室......");
                //3.侦听客户端
                Socket socket=serverSocket.accept();
                System.out.println("有新的朋友加入.....");
                //4.启动线程
                executorService.execute(new Server(socket));
            }
            //5.关闭线程池
            executorService.shutdown();
            //6.关闭服务器
            serverSocket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
